﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;

namespace Gmantis.Controls
{
    internal enum HitTestResultBehavior
    {
        Stop,
        Continue
    }

    internal abstract class HitTestResult
    {
        // Fields
        private DependencyObject _visualHit;

        // Methods
        internal HitTestResult(DependencyObject visualHit)
        {
            this._visualHit = visualHit;
        }

        // Properties
        public DependencyObject VisualHit
        {
            get
            {
                return this._visualHit;
            }
        }
    }

    internal class PointHitTestParameters
    {
        // Fields
        private Point _hitPoint;

        // Methods
        public PointHitTestParameters(Point point)
        {
            this._hitPoint = point;
        }

        internal void SetHitPoint(Point hitPoint)
        {
            this._hitPoint = hitPoint;
        }

        // Properties
        public Point HitPoint
        {
            get
            {
                return this._hitPoint;
            }
        }
    }

    internal class PointHitTestResult : HitTestResult
    {
        // Fields
        private Point _pointHit;

        // Methods
        public PointHitTestResult(UIElement visualHit, Point pointHit)
            : base(visualHit)
        {
            this._pointHit = pointHit;
        }

        // Properties
        public Point PointHit
        {
            get
            {
                return this._pointHit;
            }
        }

        public UIElement VisualHit
        {
            get
            {
                return (UIElement)base.VisualHit;
            }
        }
    }

    public static class VTreeHelper
    {
        internal delegate HitTestResultBehavior HitTestResultCallback(HitTestResult result);

        // Methods
        public static DependencyObject GetChildOfType(DependencyObject reference, Type type)
        {
            DependencyObject obj2 = null;
            if (VisualTreeHelper.GetChildrenCount(reference) > 0)
            {
                for (obj2 = VisualTreeHelper.GetChild(reference, 0); obj2 != null; obj2 = VisualTreeHelper.GetChild(obj2, 0))
                {
                    if (type.IsAssignableFrom(obj2.GetType()))
                    {
                        return obj2;
                    }
                    if (VisualTreeHelper.GetChildrenCount(obj2) <= 0)
                    {
                        break;
                    }
                }
            }
            return null;
        }

        public static void GetChildrenOfType(DependencyObject reference, Type type, ref IList<DependencyObject> list)
        {
            DependencyObject item = null;
            int childrenCount = VisualTreeHelper.GetChildrenCount(reference);
            for (int i = 0; i < childrenCount; i++)
            {
                item = VisualTreeHelper.GetChild(reference, i);
                if (item != null)
                {
                    if (type.IsAssignableFrom(item.GetType()))
                    {
                        list.Add(item);
                    }
                    else if (VisualTreeHelper.GetChildrenCount(item) > 0)
                    {
                        GetChildrenOfType(item, type, ref list);
                    }
                }
            }
        }

        public static DependencyObject GetFirstParent(DependencyObject reference, IList<Type> types, DependencyObject endObject)
        {
            if (reference == null)
            {
                return null;
            }
            while (true)
            {
                DependencyObject parent = VisualTreeHelper.GetParent(reference);
                if ((parent == null) || (parent == endObject))
                {
                    return null;
                }
                Type c = parent.GetType();
                foreach (Type type2 in types)
                {
                    if (type2.IsAssignableFrom(c))
                    {
                        return parent;
                    }
                }
                reference = parent;
            }
        }

        public static DependencyObject GetParentOfType(DependencyObject reference, Type type)
        {
            return GetParentOfType(reference, type, null, false);
        }

        public static DependencyObject GetParentOfType(DependencyObject reference, Type type, bool lookOutsideVisualTree)
        {
            return GetParentOfType(reference, type, null, lookOutsideVisualTree);
        }

        public static DependencyObject GetParentOfType(DependencyObject reference, Type type, DependencyObject endObject)
        {
            return GetParentOfType(reference, type, endObject, false);
        }

        public static DependencyObject GetParentOfType(DependencyObject reference, Type type, DependencyObject endObject, bool lookOutsideVisualTree)
        {
            if (reference == null)
            {
                return null;
            }
            while (true)
            {
                DependencyObject parent = VisualTreeHelper.GetParent(reference);
                if ((parent == null) && lookOutsideVisualTree)
                {
                    FrameworkElement element = reference as FrameworkElement;
                    if (element != null)
                    {
                        parent = element.Parent;
                    }
                }
                if ((parent == null) || (parent == endObject))
                {
                    return null;
                }
                if (type.IsAssignableFrom(parent.GetType()))
                {
                    return parent;
                }
                reference = parent;
            }
        }

        internal static void HitTest(UIElement reference, object alwaysNull, HitTestResultCallback resultCallback, PointHitTestParameters hitTestParameters)
        {
            if (reference != null)
            {
                foreach (UIElement element in VisualTreeHelper.FindElementsInHostCoordinates(reference.TransformToVisual(Application.Current.RootVisual).Transform(hitTestParameters.HitPoint), reference))
                {
                    PointHitTestResult result = new PointHitTestResult(element, hitTestParameters.HitPoint);
                    if (resultCallback(result) == HitTestResultBehavior.Stop)
                    {
                        break;
                    }
                }
            }
        }
    }
}
